Posts

Post not yet marked as solved
1 Replies
Well, since nobody else seems to be able to answer this question, I will do it myself… It turns out I was using the wrong QLThumbnailGenerator. override func prepareForReuse() { super.prepareForReuse() if let request = self.request { thumbnailGenerator.cancel(request) self.request = nil } imageView?.image = NSImage(imageLiteralResourceName: "Placeholder") The problem was so subtle. One thing I didn't quote in my original post was that I had a var that holds a private QLThumbnailGenerator instance and it was this that I was using to cancel the request, even though I was using the shared instance in the loadImage method. As soon as I switched the loadImage method to use the private var, everything started to work better. I say better because there was still the occasion when the generator would return a nil image for some obscure reason. So, for those times, I have a helper method that loads those thumbnails the "old fashioned" way… static func previewOfFile(at url: URL, size: NSSize, asIcon: Bool = false, completion: @escaping (NSImage) -> ()) { let cfUrl = url as CFURL guard let imageSource = CGImageSourceCreateWithURL(cfUrl, nil), let image = getImage(for: imageSource, at: url, size: size) else { completion(NSWorkspace.shared.icon(forFile: url.path)) return } completion(image) } … and the full code now reads… let thumbnailGenerator = QLThumbnailGenerator() var request: QLThumbnailGenerator.Request? func loadImage() { imageView?.image = NSImage(imageLiteralResourceName: "Placeholder") request = QLThumbnailGenerator.Request(fileAt: self.url!, size: self.imageSize, scale: 1.0, representationTypes: [.lowQualityThumbnail]) self.thumbnailGenerator.generateRepresentations(for: self.request!) { (thumbnail: QLThumbnailRepresentation?, type: QLThumbnailRepresentation.RepresentationType, error: Error?) -> Void in DispatchQueue.main.async { let transition = CATransition() transition.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut) transition.duration = 0.5 self.imageView?.layer?.add(transition, forKey: nil) self.imageView?.image = thumbnail?.nsImage if thumbnail?.nsImage == nil { DispatchQueue.main.async { ThumbnailImage.previewOfFile(at: self.url!, size: self.imageSize) { [unowned self] retrievedImage in transition.duration = 0.3 self.imageView?.image = retrievedImage } } } I hope this is of use to anyone else who stumbles across this issue
Post not yet marked as solved
3 Replies
Because it crashes if you don't. Something to do with unbalanced calls to resume and suspend apparently.
Post not yet marked as solved
3 Replies
Thank you Claude for your reply. When I designed the storyboard, I hadn't added a window controller for each of the segued view controllers and hadn't realised that it would be necessary just to save the position of them. I have added them and created a NSWindowController subclass to add in the necessary code.
Post not yet marked as solved
2 Replies
Nope. Just the code I cited for the panel and using QLThumbnailImageCreate for the images in the collection view items.The problem is completely solved by setting the URL for the collection item from the collection view controller's data source, then calling QLThumbnailImageCreate on a background thread within the item, followed by updating the image view in the item on completion.It's just weird that you can "abuse" QLThumbnailImageCreate in the main thread, as long as you never invoke a QLPreviewPanel 😕Thanks for your input anyway. Let's hope this thread is useful to someone else.
Post not yet marked as solved
6 Replies
Ah, now that is interesting. So, as a rule, am I better off using that idea of a time interval, just in case?The guy at ExifTool also commented "I have found MacOS to be quirky when opening a number of files at the same time in Preview. I've seen similar behaviour myself with files not edited by ExifTool" This starts to feel like unpredictable behaviour that, dare I say, ressembles a bug? 😉
Post not yet marked as solved
6 Replies
Hi QuinnUpon further investigation, it would seem that I am doing something to the files that is causing this.The files in question are image files (Nikon RAW) and I am using ExifTool to add/remove keywords in the EXIF data embedded in those files.One of the parameters passed to ExifTool determines how the file is modified when the keywords are written to the file.According to the author -"ExifTool creates a temporary file when it is writing, and renames a temporary file to overwrite the original when -overwrite_original is used. Changing this to -overwrite_original_in_place instead causes the original file to be modified"I was using -overwrite_originalI tried your suggestion and, in Preview, "modified" files are opened in one window whilst those not modified are opened in another.I have since changed to using -overwrite_original_in_place and the problem has gone away; all files are passed in, in one pass, whether they have been modified or not.So, my problem is solved for my app (it also then only gives one window in Preview) but I do wonder why passing a list of URLs to the open urls method behaves differently when an original file has been overwritten by a temporary file that has been changed, rather than modifying the original file internally.At the time when the URLs are passed in, all changes have been made and saved to the files, so it's not like something's happening to them between sending them and them arriving.Since this affects any app, including Preview, is this something that should not be happening?
Post not yet marked as solved
6 Replies
OK, I've found out why the URLs are received in more than one pass.The files in question are images. Some have modified EXIF data in them, some only have the camera's EXIF data.AFAICT, the modified files arrive first, followed by the non-modified. Is this to be expected?
Post not yet marked as solved
6 Replies
My current "hack"class AppDelegate: NSObject, NSApplicationDelegate { lazy var lastImportTime: Date = .init() func application(_ application: NSApplication, open urls: [URL]) { let currentImportTime = Date() let timeInterval = currentImportTime.timeIntervalSince(lastImportTime) if timeInterval > 1 { DataProvider.clear() lastImportTime = Date() } for url in urls { DataProvider.addURL(url) } NotificationCenter.default.post(name: .onDataLoaded, object: nil) }Like I said, this seems quite hacky but it works. Could I do better?
Post not yet marked as solved
3 Replies
Oops! Must have happened when copy/pasting. I've fixed it in my post.Many thanks for looking at this Quincey
Post not yet marked as solved
3 Replies
OK, I gave up with the responder chain. All sorts of weirdities there. In the end, I attached a closure to the cell, handled in the table view controller. So much for sophistication 😎
Post not yet marked as solved
3 Replies
This is not aimed at you Quincey - this stinks of a massive bug that should have been resolved with the introduction of the newer #selector syntax that allows the specification of a fully qualified path to the handling selector. In fact, the full qualification gets totally ignored and the nearest method with the same signature gets celled instead (in this case, the one in the cell).I have succeeded by putting a closure variable on the cell class and assigning that to the controller, and that executes correctly.I have also seen a couple of articles on calling along the responder chain from the cell to get the table view controller to respond and I am also going to try this out.I suppose we might not expect this kind of failing to be addressed soon; since SwiftUI seems to be the "new shiny", I'm guessing those of us supporting legacy apps (that can't easily be moved to SwiftUI) by adding new features to them could be left high and dry.
Post not yet marked as solved
1 Replies
OK. I've finally got the menu to show by implementing canBecomeFirstResponder and canPerformAction on the cell class - Yaay!!Now, I'm getting a crash when I tap on the menu item.Does this have anything to do with how I am fulfilling the selector on a class outside of the table view / controller / cell / etc?Could this have anything to do with the fact that I have one ValueModelCellPresenter for each cell (around a dozen on the current test table view)?